#include "vtkFunctions.hpp"
#include "vtkFunctions_export.hpp"
#include "vtkConstants.hpp"
#include "meshTypeFunctions.hpp"


namespace hms::vtk
{


template<typename MeshType>
void writeDomainField(
	xml::XMLElement* cellData,
	const Field<MeshType>& field,
	const std::string& fieldName
){
	xml::XMLElement* fieldData { createDataArray(cellData) };
	fieldData->SetAttribute( "Name", fieldName.c_str() );
	fieldData->SetAttribute( nComponentsName, field.nComponents() );
	fieldData->SetAttribute( "isUniform", false );
	fieldData->SetAttribute( "isFunction", false );
	matrixToText( fieldData , field.domainValues());
}


template<typename SolverType>
void writeDomainField(
	xml::XMLDocument* doc,
	const SolverBase<SolverType>& solver,
	typename SolverBase<SolverType>::FieldName fieldName
){
	using S = SolverBase<SolverType>;
	using MeshType = typename S::MeshType;

	xml::XMLElement* vtk { getVtkNode(doc) };

	xml::XMLElement* piece {
		getChildNodeByName(getMeshNode<MeshType>(vtk), "Piece")
	};
	xml::XMLElement* cellData { findOrInsert(piece, "CellData") };

	std::string fieldNameStr { magic_enum::enum_name(fieldName) };

	/* see whether a data array holding the domain field data already exists,
	 * and if so, delete it.
	 * Because writeDomainField always inserts an element, without deletion,
	 * there may be a conflicting definition. */
	xml::XMLElement* fieldData { internal::getChildNodeByAttributeValue(
		cellData, "DataArray", "Name", fieldNameStr.c_str()
	) };
	if (fieldData){
		cellData->DeleteChild(fieldData);
	}
	/* now update field data */
	writeDomainField( cellData, solver.field(fieldName), fieldNameStr );
}


template<typename SolverType>
void writeDomainFields(
	xml::XMLElement* vtk,
	const SolverBase<SolverType>& solver
){
	using S = SolverBase<SolverType>;
	using MeshType = typename S::MeshType;
	using FieldName = typename S::FieldName;

	xml::XMLElement* piece {
		getChildNodeByName(getMeshNode<MeshType>(vtk), "Piece")
	};
	xml::XMLElement* cellData { piece->InsertNewChildElement("CellData") };

	for ( int i{0}; i < S::nFields ; ++i ){
		FieldName fieldName { static_cast<FieldName>(i) };
		std::string fieldNameStr {
			magic_enum::enum_name(fieldName)
		};

		writeDomainField(
			cellData,
			solver.field(fieldName),
			fieldNameStr
		);
	}
}



template<typename SolverType>
void writeDomainFields( xml::XMLDocument* doc, const SolverBase<SolverType>& solver ){
	xml::XMLElement* vtk { getVtkNode(doc) };

	writeDomainFields(vtk, solver);
}



template<typename MeshType>
void writeDefaultDomainField(
	xml::XMLElement* cellData,
	const Field<MeshType>& field,
	const std::string& fieldName
){
	xml::XMLElement* fieldData { createDataArray(cellData) };
	fieldData->SetAttribute( "Name", fieldName.c_str() );
	fieldData->SetAttribute( nComponentsName, field.nComponents() );
	fieldData->SetAttribute( "isUniform", true );
	fieldData->SetAttribute( "isFunction", false );
	ArrayXs vals ( field.nComponents() );
	vals = 0;
	matrixToText( fieldData , vals );
}

template<typename SolverType>
void writeDefaultDomainFields( xml::XMLDocument* doc ){
	xml::XMLElement* vtk { getVtkNode(doc) };

	using S = SolverBase<SolverType>;
	using MeshType = typename S::MeshType;
	using FieldName = typename S::FieldName;

	xml::XMLElement* piece {
		getChildNodeByName(getMeshNode<MeshType>(vtk), "Piece")
	};
	xml::XMLElement* cellData { piece->InsertNewChildElement("CellData") };

	auto dummyMesh { uniMeshAs<MeshType>(2,2,1,1) };
	SolverType solver { dummyMesh };

	for ( int i{0}; i < S::nFields ; ++i ){
		FieldName fieldName { static_cast<FieldName>(i) };
		std::string fieldNameStr {
			magic_enum::enum_name(fieldName)
		};

		writeDefaultDomainField(
			cellData,
			solver.field(fieldName),
			fieldNameStr
		);
	}
}

} // namespace hms::vtk
